package com.ittao.taoaicodecommon.manage.storage.strategy.impl;

import com.ittao.taoaicodecommon.config.AppStorageConfig;
import com.ittao.taoaicodecommon.config.CosClientConfig;
import com.ittao.taoaicodecommon.manage.storage.enums.StorageType;
import com.ittao.taoaicodecommon.manage.storage.lifecycle.tracker.SessionActivityTracker;
import com.ittao.taoaicodecommon.manage.storage.strategy.FileStorageStrategy;
import com.ittao.taoaicodecommon.manage.storage.service.CosOperationService;
import com.ittao.taoaicodecommon.manage.storage.service.OperationTypeService;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.GetObjectRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Executor;

/**
 * 腾讯云COS文件存储策略实现
 * 提供基于腾讯云对象存储的文件管理功能
 */
@Slf4j
@Component
@ConditionalOnBean(COSClient.class)
public class CosFileStorageStrategy implements FileStorageStrategy {

    private static final String USER_APP_PREFIX = "userApp";

    @jakarta.annotation.Resource
    private OperationTypeService operationTypeService;

    @jakarta.annotation.Resource
    private CosOperationService cosOperationService;

    @jakarta.annotation.Resource
    private COSClient cosClient;

    @jakarta.annotation.Resource
    private CosClientConfig cosClientConfig;

    @jakarta.annotation.Resource
    private AppStorageConfig appStorageConfig;

    @jakarta.annotation.Resource
    private SessionActivityTracker sessionActivityTracker;

    @jakarta.annotation.Resource
    private Executor virtualThreadExecutor;

    /**
     * 从本地路径构建COS键
     * @param localPath 本地文件路径
     * @return COS键
     */
    private String buildCosKeyFromLocalPath(String localPath) {
        try {
            // 将本地路径转换为COS路径格式
            // 移除系统路径前缀，保留相对路径部分
            String tempDirPath = System.getProperty("user.dir") + appStorageConfig.getCos().getTempDirPath();

            if (localPath.startsWith(tempDirPath)) {
                String relativePath = localPath.substring(tempDirPath.length());
                // 标准化路径分隔符为 /
                String normalizedPath = relativePath.replace(File.separator, "/").replaceFirst("^/", "");
                // 添加 userApp 前缀，确保路径格式为 userApp/{userId}/...
                return USER_APP_PREFIX + "/" + normalizedPath;
            }

            // 如果不是临时目录路径，直接返回文件名
            return new File(localPath).getName();
        } catch (Exception e) {
            log.error("构建COS键失败: {}", localPath, e);
            return new File(localPath).getName();
        }
    }

    @Override
    public StorageType getStorageType() {
        return StorageType.COS;
    }

    @Override
    public void writeFile(String filePath, byte[] content) throws IOException {
        writeFile(filePath, content, null);
    }

    /**
     * 写入文件（支持应用ID参数用于对话历史判断）
     * @param filePath 文件路径
     * @param content 文件内容
     * @param appId 应用ID（可选，用于判断是否为初次对话）
     * @throws IOException IO异常
     */
    public void writeFile(String filePath, byte[] content, Long appId) throws IOException {
        try {
            // 1. 判断是否为修改操作（基于对话历史）
            boolean isModificationOperation = operationTypeService.isModificationOperation(filePath, appId);

            if (isModificationOperation) {
                // 2. 修改操作：先检查并下载项目到本地临时目录
                handleModificationOperation(filePath);
            }

            // 3. 写入本地临时文件
            Path localTempFile = Paths.get(filePath);
            Files.createDirectories(localTempFile.getParent());
            Files.write(localTempFile, content);
            log.debug("文件写入本地临时路径成功: {}", filePath);

            // 4. 发布项目修改事件（用户活动）
            if (appId != null) {
                String sessionId = operationTypeService.extractSessionIdFromPath(filePath);
                if (sessionId != null) {
                    String userId = operationTypeService.extractUserIdFromPath(filePath);
                    sessionActivityTracker.publishProjectModification(sessionId, userId, String.valueOf(appId), filePath);
                }
            }

            // 注意：不再进行即时上传，只在npm构建完成后进行批量上传
            log.debug("文件写入完成，等待npm构建完成后统一上传: {}", filePath);

        } catch (Exception e) {
            log.error("写入文件失败: {}", filePath, e);
            throw new IOException("写入文件失败: " + filePath, e);
        }
    }

    /**
     * 处理修改操作：下载整个项目到本地临时目录
     * @param filePath 当前文件路径
     */
    private void handleModificationOperation(String filePath) {
        try {
            // 提取项目目录路径
            Path projectDir = extractProjectDirectory(filePath);
            if (projectDir == null) {
                log.warn("无法提取项目目录，跳过下载: {}", filePath);
                return;
            }

            // 检查本地项目目录是否已存在
            if (Files.exists(projectDir) && Files.isDirectory(projectDir)) {
                log.debug("本地项目目录已存在，跳过下载: {}", projectDir);
                return;
            }

            // 从COS下载整个项目到本地临时目录
            String cosKey = buildCosKeyFromLocalPath(filePath);
            String projectCosKey = extractProjectCosKey(cosKey);

            if (projectCosKey != null) {
                log.info("开始从COS下载项目到本地临时目录: {} -> {}", projectCosKey, projectDir);

                // 确保本地目录存在
                Files.createDirectories(projectDir);

                // 使用高性能并发下载方法
                cosOperationService.downloadDirectoryFromCosConcurrent(projectCosKey, projectDir.toString(), null);

                log.info("项目下载完成: {}", projectDir);
            }
        } catch (Exception e) {
            log.error("处理修改操作失败: {}", filePath, e);
            // 下载失败不影响写入操作，只是没有历史文件
        }
    }

    /**
     * 从文件路径提取项目目录
     * 路径格式：.../userId/code_output/projectName/具体文件
     * @param filePath 文件路径
     * @return 项目目录路径
     */
    private Path extractProjectDirectory(String filePath) {
        try {
            Path path = Paths.get(filePath);
            Path parent = path.getParent();

            // 向上查找到code_output的子目录（项目目录）
            while (parent != null) {
                if (parent.getParent() != null &&
                        parent.getParent().getFileName() != null &&
                        "code_output".equals(parent.getParent().getFileName().toString())) {
                    return parent;
                }
                parent = parent.getParent();
            }

            return null;
        } catch (Exception e) {
            log.error("提取项目目录失败: {}", filePath, e);
            return null;
        }
    }

    /**
     * 从COS key提取项目级别的key
     * COS key格式：userApp/{userId}/code_output/{projectName}/{具体文件}
     * 项目key格式：userApp/{userId}/code_output/{projectName}
     * @param cosKey COS key
     * @return 项目级别的COS key
     */
    private String extractProjectCosKey(String cosKey) {
        try {
            String[] parts = cosKey.split("/");
            if (parts.length >= 4 && "userApp".equals(parts[0]) && "code_output".equals(parts[2])) {
                // userApp/{userId}/code_output/{projectName}
                return String.join("/", parts[0], parts[1], parts[2], parts[3]);
            }
            return null;
        } catch (Exception e) {
            log.error("提取项目COS key失败: {}", cosKey, e);
            return null;
        }
    }

    @Override
    public Resource readFile(String filePath) throws IOException {
        try {
            log.info("COS策略读取文件: {}", filePath);

            // 1. 优先检查本地临时文件是否存在
            Path localPath = Paths.get(filePath);
            if (Files.exists(localPath) && Files.isRegularFile(localPath)) {
                log.debug("读取本地临时文件: {}", filePath);
                return new FileSystemResource(localPath);
            }

            // 2. 本地文件不存在，优先映射线上COS内容
            log.info("本地临时文件不存在，优先映射线上COS内容");

            String cosKey = buildCosKeyFromLocalPath(filePath);

            // 直接从COS获取文件内容
            Resource cosResource = readFileDirectlyFromCos(cosKey);

            // 3. 异步下载整个项目到本地（为后续访问做缓存）
            asyncDownloadProjectToLocal(filePath);

            log.debug("成功从COS读取文件: {}", filePath);
            return cosResource;

        } catch (Exception e) {
            log.error("从COS读取文件失败: {}", filePath, e);
            throw new IOException("从COS读取文件失败: " + filePath, e);
        }
    }

    /**
     * 异步下载整个项目到本地（为后续访问做缓存）
     * @param filePath 文件路径
     */
    private void asyncDownloadProjectToLocal(String filePath) {
        // 使用虚拟线程异步执行下载任务
        virtualThreadExecutor.execute(() -> {
            try {
                log.debug("开始异步下载项目到本地: {}", filePath);

                // 提取项目目录路径
                Path projectDir = extractProjectDirectory(filePath);
                if (projectDir == null) {
                    log.warn("无法提取项目目录，跳过异步下载: {}", filePath);
                    return;
                }

                // 检查本地项目目录是否已存在
                if (Files.exists(projectDir) && Files.isDirectory(projectDir)) {
                    log.debug("本地项目目录已存在，跳过异步下载: {}", projectDir);
                    return;
                }

                // 从COS下载整个项目到本地临时目录
                String cosKey = buildCosKeyFromLocalPath(filePath);
                String projectCosKey = extractProjectCosKey(cosKey);

                if (projectCosKey == null) {
                    log.warn("无法提取项目COS key，跳过异步下载: {}", cosKey);
                    return;
                }

                log.info("开始异步下载项目到本地临时目录: {} -> {}", projectCosKey, projectDir);

                // 确保本地目录存在
                Files.createDirectories(projectDir);

                // 使用高性能并发下载方法
                cosOperationService.downloadDirectoryFromCosConcurrent(projectCosKey, projectDir.toString(), null);

                log.info("异步下载项目完成: {}", projectDir);

            } catch (Exception e) {
                log.warn("异步下载项目失败（不影响当前请求）: {}, 错误信息: {}", filePath, e.getMessage());
                // 异步下载失败不影响当前请求的响应
            }
        });
    }

    /**
     * 直接从COS读取文件内容并返回Resource
     * @param cosKey COS文件键
     * @return 文件Resource
     * @throws IOException IO异常
     */
    private Resource readFileDirectlyFromCos(String cosKey) throws IOException {
        try {
            log.debug("直接从COS读取文件: {}", cosKey);

            // 创建获取对象请求
            GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), cosKey);
            COSObject cosObject = cosClient.getObject(getObjectRequest);

            // 读取文件内容到字节数组
            byte[] fileContent;
            try (var inputStream = cosObject.getObjectContent()) {
                fileContent = inputStream.readAllBytes();
            }

            // 创建基于字节数组的Resource
            return new ByteArrayResource(fileContent) {
                @Override
                public String getFilename() {
                    // 从cosKey中提取文件名
                    String[] parts = cosKey.split("/");
                    return parts[parts.length - 1];
                }
            };

        } catch (CosServiceException e) {
            log.error("COS服务异常，读取文件失败: {}, 错误码: {}, 错误信息: {}",
                    cosKey, e.getErrorCode(), e.getErrorMessage());
            throw new IOException("COS服务异常: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            log.error("COS客户端异常，读取文件失败: {}, 错误信息: {}", cosKey, e.getMessage());
            throw new IOException("COS客户端异常: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("读取COS文件时发生未知异常: {}, 错误信息: {}", cosKey, e.getMessage());
            throw new IOException("读取COS文件失败: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFile(String filePath) throws IOException {
        try {
            cosOperationService.deleteFile(filePath);
            log.debug("从COS删除文件成功: {}", filePath);
        } catch (Exception e) {
            log.error("从COS删除文件失败: {}", filePath, e);
            throw new IOException("从COS删除文件失败: " + filePath, e);
        }
    }

    @Override
    public String buildCodeOutputPath(String userId, String fileName) {
        return System.getProperty("user.dir") + appStorageConfig.getCos().getTempDirPath() + File.separator + userId + File.separator + "code_output" + File.separator + fileName;
    }

    @Override
    public String buildCodeDeployPath(String userId, String deployKey) {
        return System.getProperty("user.dir") + appStorageConfig.getCos().getTempDirPath() + File.separator + userId + File.separator + "code_deploy" + File.separator + deployKey;
    }

    /**
     * 构建COS代码输出路径（线上路径）- 支持不同代码类型
     * 格式：cosClientConfig前缀 + userApp/{userId}/code_output/{codeType}_{appId}/{具体文件}
     * @param userId 用户ID
     * @param appId 应用ID
     * @param codeType 代码类型（html, multi_file, vue_project）
     * @param fileName 文件名
     * @return COS路径
     */
    public String buildCosCodeOutputPath(String userId, String appId, String codeType, String fileName) {
        // 获取cosClientConfig配置的前缀
        String prefix = cosClientConfig.getHost(); // 使用host作为前缀
        return prefix + "/" + USER_APP_PREFIX + "/" + userId + "/" + "code_output" + "/" + codeType + "_" + appId + "/" + fileName;
    }

    @Override
    public String buildDisplayPath(String baseDirPath, String userId, String appId, String codeType) {
        // COS存储：返回COS线上路径
        return buildCosCodeOutputPath(userId, appId, codeType, "");
    }

}